/**
 * @file     main.cpp
 * @author   Han Yaoting
 * @date     Fri Oct 27 2023
 * 
 * @brief    Homework3 of D.S.A. Design a four-rule hybrid calculator.
 */

#include <iostream>
#include <stack>
#include <queue>
#include <string>
#include <algorithm>

using namespace std;


/**
 * @brief   If the expression is illegal, then gives a error message and ends the program
 * 
 */
void report_Error(){
    std::cout << "Error. The expression is illegal." << std::endl;
    exit(0);
}


/**
 * @brief   If a divisor in the expression is 0, then gives a error message and ends the program
 * 
 */
void report_Error_zero(){
    std::cout << "Error. One of the divisors in the expression is 0." << std::endl;
    exit(0);
}


/**
 * @brief   Determine the priority of the operator
 * 
 * @param   op Operator
 * @return  int Priority, the higher the number, the higher the priority
 */
int Precedence(char op) {       //Check! Works!
    if (op == '+' || op == '-')
        return 1;
    if (op == '*' || op == '/')
        return 2;
    return 0;
}


/**
 * @brief   Calculate the result of the operation of two numbers
 * 
 * @param   num_1 1st number
 * @param   num_2 2nd number
 * @param   op    Operator
 * @return  double Result of the operation
 */
double calculate(double num_1, double num_2, string op) { //Check!  Works!
    if (op == "+") return num_1 + num_2;
    if (op == "-") return num_1 - num_2;
    if (op == "*") return num_1 * num_2;
    if (op == "/") {
        if (num_2 == 0) report_Error_zero();  /**< Check legitimacy */
        return num_1 / num_2;
    }
    else{
        report_Error();
    }
}


/**
 * @brief   Remove invalid characters from the input 
 * 
 * @param   input The string given in input.txt
 * @return  The string without invalid characters
 */
std::string remove_Invalid_Chars(const std::string& input) {    //Check! Works!
    std::string validChars = "0123456789.+-*/() ";
    std::string infix;
    std::copy_if(input.begin(), input.end(), std::back_inserter(infix), [&](char ch) { return validChars.find(ch) != std::string::npos; });
    return infix;
}


/**
 * @brief   Converts an infix expression to a suffix expression
 * 
 * @param   infix   The infix expression string
 * @param   postfix The queue that stores the suffix expression
 * @return  true The conversion is successful
 * @return  false The conversion failed and the expression is invalid
 */
std::queue<std::string> Infix_To_Postfix(const std::string& infix) { //Check!  Works!
    
    std::queue<std::string> postfix_Queue;  /**< Queue of Postfix */
    std::stack<char> op_Stack;              /**< Stack of Opreator */
    std::string tem_Number;                 /**< Current Number */
    
    bool Number_found   = false;            /**< Check whether the nearest position exisits valid Number or not */     
    bool Point_found    = false;            /**< Check whether the nearest position exisits valid Point or not */
    bool Operator_found = false;            /**< Check whether the nearest position exisits valid Operator or not*/
    bool Lbracket_found = false;            /**< Check whether the nearest position exisits valid Left Bracket or not */
    bool Rbracket_found = false;            /**< Check whether the nearest position exisits valid Right Bracket or note */
    bool Point_has      = false;            /**< Check if the expression has point before we push the tem_Number or not */

    bool valid_Check    = true;             /**< Check whether the expression is valid or not */

    int Lbracket_Numbers = 0;               /**< Count the Number of Left Bracket */
    int Rbracket_Numbers = 0;               /**< Count the Number of Right Bracket */

    for (char ch : infix) {
        if (isdigit(ch)) {              /**< If ch is a number from 1-9 */
            tem_Number += ch;           /**< Mark the number */
            Number_found   = true;      /**< Change the value of bool functions */
            Operator_found = false;
            Lbracket_found = false;
        }

        else if (ch == '.') {
            if (!Number_found || Operator_found || Point_found || Point_has) {  /**< Check legitimacy */
                valid_Check = false;
                break;
            }
            tem_Number += ch;
            Point_found  = true;
            Number_found = false;
            Point_has    = true;
        }

        else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') {
            if (!Number_found || Lbracket_found) {  /**< Check legitimacy */
                valid_Check = false;
                break;
            }
            if (!tem_Number.empty()) {      /**< push the tem_Number which is not empty into postfix_Queue */
                postfix_Queue.push(tem_Number);
                tem_Number.clear();
                Number_found = false;
                Point_found  = false;
                Point_has    = false;
            }
            while (!op_Stack.empty() && op_Stack.top() != '(' && Precedence(op_Stack.top()) >= Precedence(ch)) {   /**< Remove all operators in the operator stack with a higher priority than the currently traversed operator ch and add to the postfix expression queue */
                postfix_Queue.push(std::string(1, op_Stack.top()));
                op_Stack.pop();
            }
            op_Stack.push(ch);
            Operator_found = true;
            Point_found    = false;
        }

        else if(ch == '(') {
            if (Number_found || Point_found) {  /**< Check legitimacy */
                valid_Check = false;
                break;
            }
            if (!tem_Number.empty()){
                postfix_Queue.push(tem_Number);
                tem_Number.clear();
                Number_found   = true;
                Operator_found = false;
            }
            op_Stack.push(ch);      /**< Push left bracket into op_Stack */
            Lbracket_Numbers++;     /**< The number of left bracket +1 */
            Lbracket_found = true;  
        }

        else if (ch == ')') {
            if (Operator_found || !Number_found) {    /**< Check legitimacy */
                valid_Check = false;
                break;
            }
            if (!tem_Number.empty()) {
                postfix_Queue.push(tem_Number);
                tem_Number.clear();
                Number_found = true;
            }
            while (!op_Stack.empty() && op_Stack.top() != '(') {    /**< Push the expression in the bracket */
                postfix_Queue.push(std::string(1, op_Stack.top()));
                op_Stack.pop();
            }
            if (!op_Stack.empty() && op_Stack.top() == '(') {
                op_Stack.pop();
                Rbracket_Numbers++;
            }
        }

    }
        if (Lbracket_Numbers != Rbracket_Numbers || !valid_Check) { /**< Check legitimacy */
            report_Error();
        }

        if (!tem_Number.empty()) {  /**< Prevent individual numbers from being missed */
            postfix_Queue.push(tem_Number);
        }   

        while (!op_Stack.empty()) { /**< Push the operator into postfix_Queue */
            postfix_Queue.push(std::string(1, op_Stack.top()));
            op_Stack.pop();
        }

        return postfix_Queue;
}

/**
 * @brief   Calculates the value of the postfix expression
 * 
 * @param   postfix The queue that stores the suffix expression
 * @return  double Result of opreation
 */
double calculate_Postfix(const std::queue<string>& postfix) { //Check!  Works!

    std::stack<double> result_Stack;                /**< Number stack */
    std::queue<string> postfix_Buffer(postfix);     /**< Buffer of postfix queue */

    while (!postfix_Buffer.empty()) {      
        
        std::string Buffer = postfix_Buffer.front();/**< Take out the first string */
        postfix_Buffer.pop();

        if (isdigit(Buffer[0]) || (Buffer[0] == '-' && Buffer.length() > 1)) {/**< Handle strings that are numbers */
            double num = std::stod(Buffer);
            result_Stack.push(num);
        }

        else {
            if (result_Stack.size() < 2) { /**< Check legitimacy */
                report_Error();
            }
            double num_2 = result_Stack.top();  /**< Calculations are made for each part */
            result_Stack.pop();
            double num_1 = result_Stack.top();
            result_Stack.pop();
            result_Stack.push(calculate(num_1, num_2, Buffer));
        }
    }

    if (result_Stack.size() != 1) {
        report_Error();                   /**< The psotfix expression is incorrect */
    }
    return result_Stack.top();
}


int main() {

    std::string input;
    std::getline (std::cin, input);  /**< Get expression */

    std::string infix = remove_Invalid_Chars (input); //Check!  Works!

    std::queue<string> postfix = Infix_To_Postfix (infix);

    double result = calculate_Postfix(postfix);
    cout << result << endl;

    return 0;
}